package com.m1.tzolkin.model.wheels;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.LinkedHashMap;
import java.util.Map;

import com.m1.tzolkin.model.IAction;
import com.m1.tzolkin.model.exceptions.NoPlaceException;
import com.m1.tzolkin.model.players.IWorker;
import com.m1.tzolkin.model.players.Worker;

public class AbsWheel implements IWheel {
	
	// ATTRIBUTS
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1981870936058782872L;
	private final String name;
	private final IWorker[] places;
	private final IAction[] actions;
	private final int bonus;
	private final PropertyChangeSupport listeners;
	
	// CONSTRUCTEUR
	
	AbsWheel(String s, int n, int bonus, IAction[] actions) {
		if (s == null || s.trim().equals("") || bonus < 0) {
			throw new IllegalArgumentException();
		}
		// Nom de la roue
		name = s;
		// Nombre de places (non jouables incluses)
		places = new IWorker[n];
		this.bonus = bonus;
		this.actions = actions;
		listeners = new PropertyChangeSupport(this);
	}
	
	// REQUETES
	
	@Override
	public PropertyChangeListener[] getPropertyChangeListeners() {
		return listeners.getPropertyChangeListeners();
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public int getFullLength() {
		return places.length;
	}

	@Override
	public int getCircuitLength() {
		return EMPTY_ACTIONS + actions.length + bonus;
	}

	@Override
	public int getBonusLength() {
		return bonus;
	}

	@Override
	public int getMinimalPosition() throws NoPlaceException {
		for (int i = 0; i < EMPTY_ACTIONS + actions.length + bonus; ++i) {
			if (places[i] == null) {
				return i;
			}
		}
		throw new NoPlaceException();
	}

	@Override
	public IWorker getWorker(int i) {
		if (i < 0 || i >= places.length) {
			throw new IllegalArgumentException("i = " + i);
		}
		return places[i];
	}

	@Override
	public boolean canMultipleTurn(int i) {
		if (i < 2) {
			throw new IllegalArgumentException("i = " + i);
		}
		// On remonte la roue pour voir s'il y a un ouvrier entre la fin et i.
		// j pointe sur la case � checker
		// On fait - 1 car on ne v�rifie pas la toute derni�re case.
		IWorker p;
		int j = EMPTY_ACTIONS + actions.length + bonus - 1;
		do {
			p = places[--j];
		} while(i-- > 0 && (p == null || p.getOwner() == null));
		return i == 0;
	}

	@Override
	public boolean isForbidden() {
		return false;
	}

	@Override
	public IAction[] getActions() {
		return actions;
	}

	// COMMANDES
	
	@Override
	public void addPropertyChangeListener(PropertyChangeListener l) {
		listeners.addPropertyChangeListener(l);
	}
	
	@Override
	public void removePropertyChangeListener(PropertyChangeListener l) {
		listeners.removePropertyChangeListener(l);
	}
	
	@Override
	public void block(int i) {
		places[i] = new Worker();
		listeners.firePropertyChange("block", -1, i);
	}
	
	@Override
	public void placeWorker(IWorker w) throws NoPlaceException {
		if (w == null) {
			throw new IllegalArgumentException();
		}
		int pos = getMinimalPosition();
		places[pos] = w;
		listeners.firePropertyChange("place", -1, pos);
	}

	@Override
	public Map<IAction, Integer> removeWorker(int i) {
		if (i < 0 || i >= EMPTY_ACTIONS + actions.length + bonus || places[i] == null) {
			throw new IllegalArgumentException("i = "+ i);
		}
		Map<IAction, Integer> a = new LinkedHashMap<IAction, Integer>();
		// Si on est pas sur une case sans action
		if (i >= EMPTY_ACTIONS) {
			// Si i dans les actions possibles
			if (i < EMPTY_ACTIONS + actions.length) {
				for (int j = 0; j <= i - EMPTY_ACTIONS; ++j) {
					if (actions[j].isAvailable()) {
						a.put(actions[j], i - j - EMPTY_ACTIONS);
					}
				}
			} else {
				// Si on est dans la zone bonus, on donne toutes les actions à coût 0.
				for (IAction act : actions) {
					if (act.isAvailable()) {
						a.put(act, 0);
					}
				}
			}
		}
		places[i].getOwner().backPawn(places[i]);
		places[i] = null;
		listeners.firePropertyChange("remove", -1, i);
		return a;
	}

	@Override
	public void turnDay() {
		int last = getCircuitLength() - 1;
		if (places[last] != null && places[last].getOwner() != null) {
			removeWorker(last);
		}
		// Enregistre le player en 0, pour le remettre à la fin de la boucle
		IWorker p = places[0];
		int c = getFullLength();
		for (int i = c - 1; i > 0; --i) {
			places[(i + 1) % c] = places[i];
		}
		places[1] = p;
		listeners.firePropertyChange("pos", null, null);
	}

}
